using System;
using System.IO;
using System.Net;
using System.Reflection;
using System.Xml;
using Microsoft.Crm.Sdk;
using Microsoft.Crm.Sdk.Query;
using Microsoft.Crm.SdkTypeProxy;
using NAnt.Core;
using NAnt.Core.Attributes;

namespace NAnt.Microsoft.CRM.ThirdParty.Tasks
{

    [TaskName("deployplugins")]
    public class DeployPluginsTask : Task
    {

        [TaskAttribute("crmserver", Required = true)]
        [StringValidator(AllowEmpty = false)]
        public string CrmServer { get; set; }

        [TaskAttribute("username", Required = true)]
        [StringValidator(AllowEmpty = false)]
        public string Username { get; set; }

        [TaskAttribute("password", Required = true)]
        [StringValidator(AllowEmpty = false)]
        public string Password { get; set; }

        [TaskAttribute("domain", Required = true)]
        [StringValidator(AllowEmpty = false)]
        public string Domain { get; set; }

        [TaskAttribute("file", Required = true)]
        [StringValidator(AllowEmpty = false)]
        public string RegisterXmlFile { get; set; }

        [TaskAttribute("org", Required = true)]
        [StringValidator(AllowEmpty = false)]
        public string Organisation { get; set; }

        [TaskAttribute("port", Required = true)]
        [StringValidator(AllowEmpty = false)]
        public string Port { get; set; }

        [TaskAttribute("pluginfiledirectory", Required = true)]
        [StringValidator(AllowEmpty = false)]
        public string PluginDir { get; set; }

        protected override void ExecuteTask()
        {

            Project.Log(Level.Info, "Creating Connection");
            CrmAuthenticationToken token = new CrmAuthenticationToken();
            token.AuthenticationType = 0;
            token.OrganizationName = Organisation;

            CrmService crmservice = new CrmService();
            crmservice.Url = String.Format("http://{0}:{1}/mscrmservices/2007/crmservice.asmx", CrmServer, Port);
            crmservice.Credentials = new NetworkCredential(Username, Password, Domain);
            crmservice.CrmAuthenticationTokenValue = token;

            PublishPlugins(crmservice);
        }

        public void PublishPlugins(CrmService service)
        {
            XmlDocument registerdoc = new XmlDocument();
            registerdoc.Load(RegisterXmlFile);

            XmlNodeList solutionnodes = registerdoc.GetElementsByTagName("Solution");

            foreach (XmlNode solutionnode in solutionnodes)
            {
                Solution solution = new Solution(PluginDir, solutionnode);

                Project.Log(Level.Info, "Removing Existing Installation of Plugin");
                solution.DeleteDuplicates(service);

                Project.Log(Level.Info, "Registering Plugin Solution");
                RegisterSolutionRequest request = new RegisterSolutionRequest();
                request.PluginAssembly = solution.PluginAssembly;
                request.Steps = solution.Steps;
                RegisterSolutionResponse response = (RegisterSolutionResponse)service.Execute(request);

                Project.Log(Level.Info, "Plugin Solution Registered");

                Project.Log(Level.Info, "Registering Individual Plugins");

                foreach (plugintype plugintype in solution.WorkflowTypes)
                {
                    plugintype.pluginassemblyid = new Lookup();
                    plugintype.pluginassemblyid.type = EntityName.pluginassembly.ToString();
                    plugintype.pluginassemblyid.Value = response.PluginAssemblyId;
                    plugintype.isworkflowactivity = new CrmBoolean();
                    plugintype.isworkflowactivity.Value = true;
                    service.Create(plugintype);
                }

                Project.Log(Level.Info, "Registering individual plugins completed");
                Project.Log(Level.Info, "Process completed");
            }

        }
    }

    internal class Solution
    {
        public pluginassembly pluginassembly;
        public SdkMessageProcessingStepRegistration[] steps;
        public plugintype[] workflowtypes;

        public Solution(string plugindirectory, XmlNode solutionnode)
        {
            pluginassembly = new pluginassembly();
            FileInfo fi = new FileInfo(Path.Combine(plugindirectory, solutionnode.Attributes["Assembly"].Value));
            string path = fi.FullName;
            Assembly assembly = Assembly.LoadFile(path);
            byte[] bytes = File.ReadAllBytes(path);
            string[] assemblyProp = assembly.GetName().FullName.Split(",= ".ToCharArray(),
                                                                      StringSplitOptions.RemoveEmptyEntries);
            pluginassembly.content = Convert.ToBase64String(bytes);
            pluginassembly.culture = assemblyProp[4];
            pluginassembly.name = assemblyProp[0];
            pluginassembly.publickeytoken = assemblyProp[6];
            pluginassembly.sourcetype = new Picklist();
            pluginassembly.sourcetype.Value = Convert.ToInt32(solutionnode.Attributes["SourceType"].Value);
            pluginassembly.version = assemblyProp[2];

            XmlNodeList workflowtypenodes = solutionnode.SelectNodes("WorkflowTypes/WorkflowType");
            workflowtypes = new plugintype[workflowtypenodes.Count];

            if (workflowtypenodes.Count > 0)
            {
                for (int a = 0; a < workflowtypenodes.Count; a++)
                {
                    workflowtypes[a] = new plugintype();
                    workflowtypes[a].typename = workflowtypenodes[a].Attributes["TypeName"].Value;
                    workflowtypes[a].friendlyname = workflowtypenodes[a].Attributes["FriendlyName"].Value;
                }
            }

            XmlNodeList stepnodes = solutionnode.SelectNodes(".//Steps/Step");
            steps = new SdkMessageProcessingStepRegistration[stepnodes.Count];
            if (stepnodes.Count > 0)
            {
                for (int b = 0; b < stepnodes.Count; b++)
                {
                    steps[b] = new SdkMessageProcessingStepRegistration();
                    steps[b].CustomConfiguration = stepnodes[b].Attributes["CustomConfiguration"].Value;
                    steps[b].Description = stepnodes[b].Attributes["Description"].Value;
                    steps[b].InvocationSource = Convert.ToInt32(stepnodes[b].Attributes["InvocationSource"].Value);
                    steps[b].MessageName = stepnodes[b].Attributes["MessageName"].Value;
                    steps[b].Mode = Convert.ToInt32(stepnodes[b].Attributes["Mode"].Value);
                    steps[b].PluginTypeFriendlyName = stepnodes[b].Attributes["PluginTypeFriendlyName"].Value;
                    steps[b].PluginTypeName = stepnodes[b].Attributes["PluginTypeName"].Value;
                    steps[b].Stage = Convert.ToInt32(stepnodes[b].Attributes["Stage"].Value);
                    steps[b].SupportedDeployment = Convert.ToInt32(stepnodes[b].Attributes["SupportedDeployment"].Value);

                    string FilteringAttributes = stepnodes[b].Attributes["FilteringAttributes"].Value;
                    if (FilteringAttributes.Length > 0)
                    {
                        steps[b].FilteringAttributes = stepnodes[b].Attributes["FilteringAttributes"].Value;
                    }

                    string ImpersonatingUserId = stepnodes[b].Attributes["ImpersonatingUserId"].Value;
                    if (ImpersonatingUserId.Length > 0)
                    {
                        steps[b].ImpersonatingUserId = new Guid(stepnodes[b].Attributes["ImpersonatingUserId"].Value);
                    }

                    string PrimaryEntityName = stepnodes[b].Attributes["PrimaryEntityName"].Value;
                    if (PrimaryEntityName.Length > 0)
                    {
                        steps[b].PrimaryEntityName = stepnodes[b].Attributes["PrimaryEntityName"].Value;
                    }

                    string SecondaryEntityName = stepnodes[b].Attributes["SecondaryEntityName"].Value;
                    if (SecondaryEntityName.Length > 0)
                    {
                        steps[b].SecondaryEntityName = stepnodes[b].Attributes["SecondaryEntityName"].Value;
                    }

                    XmlNodeList imagenodes = stepnodes[b].SelectNodes("Images/Image");
                    if (imagenodes.Count > 0)
                    {
                        steps[b].Images = new SdkMessageProcessingStepImageRegistration[imagenodes.Count];
                        for (int c = 0; c < imagenodes.Count; c++)
                        {
                            steps[b].Images[c] = new SdkMessageProcessingStepImageRegistration();
                            steps[b].Images[c].EntityAlias = imagenodes[c].Attributes["EntityAlias"].Value;
                            steps[b].Images[c].ImageType = Convert.ToInt32(imagenodes[c].Attributes["ImageType"].Value);
                            steps[b].Images[c].MessagePropertyName = imagenodes[c].Attributes["MessagePropertyName"].Value;

                            string AttributeArray = imagenodes[c].Attributes["Attributes"].Value;
                            if (AttributeArray != null)
                            {
                                steps[b].Images[c].Attributes = AttributeArray.Split(',');
                            }

                        }
                    }
                }
            }
        }

        public pluginassembly PluginAssembly
        {
            get
            {
                return pluginassembly;
            }
        }

        public SdkMessageProcessingStepRegistration[] Steps
        {
            get
            {
                return steps;
            }
        }

        public plugintype[] WorkflowTypes
        {
            get
            {
                return workflowtypes;
            }
        }

        public int DeleteDuplicates(CrmService service)
        {
            ColumnSet cols = new ColumnSet(new string[] { "pluginassemblyid", "name" });
            ConditionExpression cond = new ConditionExpression();
            cond.AttributeName = "name";
            cond.Operator = ConditionOperator.Equal;
            cond.Values = new object[] { pluginassembly.name };
            FilterExpression filter = new FilterExpression();
            filter.FilterOperator = LogicalOperator.And;
            filter.Conditions.AddRange(new ConditionExpression[] { cond });
            QueryExpression query = new QueryExpression();
            query.ColumnSet = cols;
            query.Criteria = filter;
            query.EntityName = EntityName.pluginassembly.ToString();
            RetrieveMultipleRequest request = new RetrieveMultipleRequest();
            request.Query = query;
            RetrieveMultipleResponse response = (RetrieveMultipleResponse)service.Execute(request);
            int dupes = response.BusinessEntityCollection.BusinessEntities.Count;
            if (dupes > 0)
            {
                for (int i = 0; i < dupes; i++)
                {
                    pluginassembly pa = (pluginassembly)response.BusinessEntityCollection.BusinessEntities[i];
                    Console.WriteLine(pa.pluginassemblyid.Value.ToString());
                    UnregisterSolutionRequest unregister = new UnregisterSolutionRequest();
                    unregister.PluginAssemblyId = pa.pluginassemblyid.Value;
                    service.Execute(unregister);
                }
            }
            return dupes;
        }
    }
}